package com.longway.common.controller;

import com.alibaba.fastjson.JSONObject;
import com.jfinal.aop.Clear;
import com.jfinal.core.NotAction;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.Ret;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.upload.UploadFile;
import com.longway.common.db.id.IdManager;
import com.longway.common.kit.EnterpriseCertificationKit;
import com.longway.common.kit.ImageKit;
import com.longway.common.model.BaseAttach;
import com.longway.common.service.DbService;
import com.longway.core.toolbox.Func;
import com.longway.core.toolbox.file.UploadFileKit;
import org.joda.time.LocalDateTime;
import org.jsoup.helper.StringUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 返回的结果格式主要与kindeditor.js前端插件配合
 *
 * @author ryc
 */
public class KindeditorController extends BaseController {

    /**
     * 上传允许的最大尺寸，目前只允许 5M
     */
//    public static final int MaxSize = PropKit.getInt("uploadMaxSize", 10 * 1024 * 1024);
    public static final int MaxSize = 10000000;

    /**
     * 上传图片临时目录，相对于 baseUploadPath 的路径，是否以 "/" 并无影响
     * 本项目的 baseUploadLoad 为 /var/www/project_name/upload
     */
    public static final String uploadTempPath = "/img/temp";

    /**
     * 相对于 webRootPath 之后的目录，与"/upload" 是与 baseUploadPath 重合的部分
     */
    private static final String basePath = "/upload/";

    /**
     * 每个子目录允许存 5000 个文件
     */
    public static final int FILES_PER_SUB_DIR = 5000;

    public void upload() {
        UploadFile file = getFile();
        String uploadType = getPara("uploadType");
        String typeCode = getPara("typeCode");
        Long attachId = getParaToLong("attachId");
        String table = getPara("table");
        String dir = getPara("dir", "allfile");
        try {
            Ret ret = Ret.create();
            if (null == file) {
                ret.set("error", 1);
                ret.set("message", "请选择要上传的文件");
                renderJson(ret);
                return;
            }
            String originalFileName = file.getOriginalFileName();
            // 测试后缀
            boolean ok = UploadFileKit.testExt(dir, originalFileName);
            if (!ok) {
                file.getFile().delete();      // 非允许的文件类型，立即删除，避免浪费磁盘空间
                ret.set("error", 1);
                ret.set("message", "上传文件的类型不允许");
                renderJson(ret);
                return;
            }
            Long fileSize = file.getFile().length();
            if (fileSize > MaxSize) {
                file.getFile().delete();      // 文件大小超出范围，立即删除，避免浪费磁盘空间
                ret.set("error", 1);
                ret.set("message", "文件尺寸只允许 " + MaxSize + " 大小");
                renderJson(ret);
                return;
            }
            String extName = "." + ImageKit.getExtName(file.getFileName());

            // 相对路径 + 文件名：用于返回 ueditor 要求的 url 字段值，形如："/upload/img/project/0/123.jpg
            String[] relativePathFileName = new String[1];
            // 绝对路径 + 文件名：用于保存到文件系统
            String[] absolutePathFileName = new String[1];
            // 生成的文件名
            String[] fileName = new String[1];
            buildPathAndFileName(uploadType, getLoginAccountId(), extName, relativePathFileName, absolutePathFileName, fileName, dir);
            if ("image".equals(dir)) {
                ImageKit.zoom(500, file.getFile(), absolutePathFileName[0]);
            } else {
                saveOriginalFileToTargetFile(file.getFile(), absolutePathFileName[0]);
            }
            // 更新 upload_counter 表的 counter 字段值
            updateUploadCounter(uploadType);

            BaseAttach ba = saveAttach(uploadType, typeCode, table, attachId, file.getOriginalFileName(), relativePathFileName[0], getLoginAccountId(), fileSize);

            String fileId = String.valueOf(ba.getId());
            String url = "/admin/kindeditor/renderFile/" + fileId;
            ret.set("error", 0);
            ret.set("fileId", fileId);
            ret.set("url", url);
            ret.set("filesize", fileSize);
            ret.set("fileName", originalFileName);
            ret.set("id", fileId);
            renderJson(ret);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            file.getFile().delete();
        }

    }

    private BaseAttach saveAttach(String code, String typeCode, String tablename, Long attachId, String name, String url, Long creater, Long filesize) {
        BaseAttach ba = new BaseAttach();
        Object id = IdManager.me().getDefaultIdFactory().getId();
        ba.set("id", id);
        ba.set("code", code).set("typeCode", typeCode).set("name", name).set("url", url).
                set("creater", creater).set("createTime", new Date()).set("tableName", tablename).set("attachid", attachId).set("fileSize", filesize);
        ba.save();
        return ba;
    }

    public void upload_json() {
        UploadFile file = getFile();
        try {
            String uploadType = getPara("uploadType");
            String typeCode = getPara("typeCode");
            Long attachId = getParaToLong("attachId");
            String table = getPara("table");
            String dir = getPara("dir", "image");
            if (null == file) {
                Ret ret = Ret.create("error", 1).set("message", "请选择要上传的图片");
                renderJson(ret);
                return;
            }
            String originalFileName = file.getOriginalFileName();

            boolean ok = UploadFileKit.testExt(dir, originalFileName);
            if (!ok) {
                file.getFile().delete();      // 非允许的文件类型，立即删除，避免浪费磁盘空间
                Ret ret = Ret.create();
                ret.set("error", 1);
                ret.set("message", "上传文件的类型不允许");
                renderJson(ret);
                return;
            }
            if (UploadFileKit.testImage("image", originalFileName)) {
                ImageKit.zoom(1000, file.getFile(), file.getUploadPath() + "/" + file.getFileName());
            }
            Long fileSize = file.getFile().length();
            if (file.getFile().length() > MaxSize) {
                file.getFile().delete();      // 文件大小超出范围，立即删除，避免浪费磁盘空间
                Ret ret = Ret.create("error", 1).set("message", "文件尺寸只允许 10M大小");
                renderJson(ret);
                return;
            }

            String extName = "." + ImageKit.getExtName(file.getFileName());

            // 相对路径 + 文件名：用于返回 ueditor 要求的 url 字段值，形如："/upload/img/project/0/123.jpg
            String[] relativePathFileName = new String[1];
            // 绝对路径 + 文件名：用于保存到文件系统
            String[] absolutePathFileName = new String[1];
            // 生成的文件名
            String[] fileName = new String[1];
            buildPathAndFileName(uploadType, getLoginAccountId(), extName, relativePathFileName, absolutePathFileName, fileName, dir);
            saveOriginalFileToTargetFile(file.getFile(), absolutePathFileName[0]);
//          ImageKit.zoom(1000, file.getFile(), absolutePathFileName[0]);
            // 更新 upload_counter 表的 counter 字段值
            updateUploadCounter(uploadType);

            BaseAttach ba = saveAttach(uploadType, typeCode, table, attachId, file.getOriginalFileName(), relativePathFileName[0], getLoginAccountId(), fileSize);

            //返回kindeditor有个坑,返回的json格式 error是int类型,其它的值都必须是String 类型,不然前端会报错
            //之前fieldId 返回的文件记录的主键Id 是int直接返回的,前端会报变量var 没有 replace方法,因为返回的是int对应js number类型也就没有replace方法
            //折腾了一下午,在此备注 所以把fileId转为String类型
            String fileId = String.valueOf(ba.getId());
            String url = "/admin/kindeditor/renderFile/" + fileId;
            Ret ret = Ret.create("error", 0).set("title", originalFileName).set("url", url).set("fileId", fileId);
            set("name", originalFileName)
                    .set("filesize", fileSize)
                    .set("id", fileId);
            renderJson(ret);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            file.getFile().delete();
        }
    }

    public void upload_jsons() {
        List<UploadFile> files = getFiles();
        List<Ret> rets = new ArrayList<>();
        String uploadType = getPara("uploadType");
        String typeCode = getPara("typeCode");
        Long attachId = getParaToLong("attachId");
        String table = getPara("table");
        if (files.size() == 0) {
            Ret retFail = Ret.fail("error", 1).set("message", "请选择要上传的图片");
            renderJson(retFail);
            return;
        }
        for (UploadFile file : files) {
            try {
                if (null == file) {
                    Ret ret = Ret.create("error", 1).set("message", "请选择要上传的图片");
                    rets.add(ret);
                    renderJson(ret);
                    return;
                }
                String dir = getPara("dir", "image");
                String originalFileName = file.getOriginalFileName();
                boolean checkResult = UploadFileKit.testExt(dir, originalFileName);
                if (!checkResult) {
                    file.getFile().delete();      // 非允许的文件类型，立即删除，避免浪费磁盘空间
                    Ret ret = Ret.create();
                    ret.set("error", 1);
                    ret.set("message", "上传文件的类型不允许");
                    renderJson(ret);
                    return;
                }
                if (UploadFileKit.testImage("image", originalFileName)) {
                    if (!"seal".equals(uploadType)) {
                        com.longway.common.kit.ImageKit.zoom(1000, file.getFile(), file.getUploadPath() + "/" + file.getFileName());
                    }
                }
                Long fileSize = file.getFile().length();
                if ("doc".equals(dir)){
                    fileSize = new Long("1");
                }
                if (fileSize > MaxSize) {
                    file.getFile().delete();      // 图片大小超出范围，立即删除，避免浪费磁盘空间
                    Ret ret = Ret.create("error", 1).set("message", "文件尺寸只允许 10M 大小");
                    rets.add(ret);
                    renderJson(rets);
                    return;
                }
                Map<String, String> map = null;
                if ("gszl".equals(uploadType)) {
                    String identifyMap = EnterpriseCertificationKit.me.aliyun(files.get(0));
                    if (StringUtil.isBlank(identifyMap)) {
                        rets.add(Ret.fail("message", "企业认证失败,请稍后再试!").set("code", "500").set("error", 1));
                        renderJson(rets);
                        return;
                    }
                    Map<String, JSONObject> object = JSONObject.parseObject(identifyMap, Map.class);
                    map = JSONObject.parseObject(object.get("营业执照识别实体信息").toJSONString(), Map.class);

                    if (StringUtil.isBlank(map.get("企业名称")) || StringUtil.isBlank(map.get("企业统一社会信用编码")) || StringUtil.isBlank(map.get("企业法人"))) {
                        rets.add(Ret.fail("message", "企业认证失败,请稍后再试!").set("code", "500").set("error", 1));
                        renderJson(rets);
                        return;
                    }
                    Ret checkBaiduRet = EnterpriseCertificationKit.me.checkBaidu(map);
                    if (checkBaiduRet.isFail()) {
                        rets.add(Ret.fail("message", checkBaiduRet.get("msg")).set("code", "500").set("error", 1));
                        renderJson(rets);
                        return;
                    }
                }
                String extName = "." + ImageKit.getExtName(file.getFileName());

                // 相对路径 + 文件名：用于返回 ueditor 要求的 url 字段值，形如："/upload/img/project/0/123.jpg
                String[] relativePathFileName = new String[1];
                // 绝对路径 + 文件名：用于保存到文件系统
                String[] absolutePathFileName = new String[1];
                // 生成的文件名
                String[] fileName = new String[1];
                buildPathAndFileName(uploadType, getLoginAccountId(), extName, relativePathFileName, absolutePathFileName, fileName, dir);
                saveOriginalFileToTargetFile(file.getFile(), absolutePathFileName[0]);
//              ImageKit.zoom(1000, file.getFile(), absolutePathFileName[0]);
                // 更新 upload_counter 表的 counter 字段值
                updateUploadCounter(uploadType);

                BaseAttach ba = saveAttach(uploadType, typeCode, table, attachId, file.getOriginalFileName(), relativePathFileName[0], getLoginAccountId(), fileSize);

                //返回kindeditor有个坑,返回的json格式 error是int类型,其它的值都必须是String 类型,不然前端会报错
                //之前fieldId 返回的文件记录的主键Id 是int直接返回的,前端会报变量var 没有 replace方法,因为返回的是int对应js number类型也就没有replace方法
                //折腾了一下午,在此备注 所以把fileId转为String类型
                String fileId = String.valueOf(ba.getId());
                String url = "/admin/kindeditor/renderFile/" + fileId;
                Ret ret = Ret.create("error", 0).set("title", originalFileName).set("url", url).set("fileId", fileId)
                        .set("name", originalFileName)
                        .set("filesize", fileSize)
                        .set("id", fileId).set("message", "上传成功").set("data", map);
                rets.add(ret);
            } catch (Exception e) {
                e.printStackTrace();
                rets.add(Ret.fail("error", 1).set("message", "发生异常,请联系管理员"));
            } finally {
                file.getFile().delete();
            }
        }
        renderJson(rets);
    }


    /**
     * 生成规范的文件名
     * accountId_年月日时分秒毫秒.jpg
     * 包含 accountId 以便于找到某人上传的图片，便于定位该用户所有文件,方便清除恶意上传
     * 图片中添加一些 meta 信息：accountId_201904161359100.jpg
     * 目录中已经包含了模块名了，这里的 meta 只需要体现 accountId 与时间就可以了
     */
    @NotAction
    private String generateFileName(Long accountId, String extName) {
        LocalDateTime dt = LocalDateTime.now();
        return accountId + "_" + dt.toString("yyyyMMddHHmmssSSS") + extName;
    }

    /**
     * 根据上传类型生成完整的文件保存路径
     *
     * @param uploadType 上传类型
     */
    @NotAction
    private void buildPathAndFileName(
            String uploadType,
            Long accountId,
            String extName,
            String[] relativePathFileName,
            String[] absolutePathFileName,
            String[] fileName, String dir) {

        Integer counter = Db.queryInt("select counter from upload_counter where uploadType=? ", uploadType);
        if (counter == null) {
            throw new IllegalArgumentException("uploadType 不正确");
        }

        String relativePath = "/" + (counter / FILES_PER_SUB_DIR) + "/";    // 生成相对对路径
        relativePath = basePath + dir + "/" + uploadType + relativePath;

        fileName[0] = generateFileName(accountId, extName);
        relativePathFileName[0] = relativePath + fileName[0];

        String absolutePath = PropKit.get("baseUploadPath") + relativePath;
        File temp = new File(absolutePath);
        if (!temp.exists()) {
            temp.mkdirs();  // 如果目录不存在则创建
        }
        absolutePathFileName[0] = absolutePath + fileName[0];
    }

    /**
     * 上传完成后更新 upload_counter 表
     */
    @NotAction
    private void updateUploadCounter(String uploadType) {
        Db.update("update upload_counter set counter = counter + 1 where uploadType=? ", uploadType);
    }

    /**
     * 目前使用 File.renameTo(targetFileName) 的方式保存到目标文件，
     * 如果 linux 下不支持，或者将来在 linux 下要跨磁盘保存，则需要
     * 改成 copy 文件内容的方式并删除原来文件的方式来保存
     */
    @NotAction
    private void saveOriginalFileToTargetFile(File originalFile, String targetFile) {
        originalFile.renameTo(new File(targetFile));
    }

    public void file_manager_json() {
        String dir = getPara("dir", "image");
        // 考虑安全问题
        String path = getPara("path", "");
        // 不允许使用..移动到上一级目录
        if (path.indexOf("..") >= 0) {
            renderText("Access is not allowed.");
            return;
        }
        // 最后一个字符不是/
        if (!"".equals(path) && !path.endsWith("/")) {
            renderText("Parameter is not valid.");
            return;
        }
        String order = getPara("order", "name");
        Map<String, Object> result = UploadFileKit.listFiles(dir, path, order);
        renderJson(result);
    }

    public void initimg() {
        Record record = Db.findFirst("select * from base_attach where id = ?", getPara("id"));
        if (null != record) {
            renderJson(ajaxSucess(record));
        }
    }

    public void initfile() {
        String ids = getPara("ids");
        List<Record> files = Db.find("select * from base_attach where id in (" + ids + ")");
        if (null != files) {
            renderJson(ajaxSucess(files));
        }
    }

    @Clear
    public void renderFile() {
        String id = getPara(0);
        Record file = Db.findFirst("select * from base_attach where id = ?", id);
        if (Func.isEmpty(file)) {
            redirect("/error/error404");
            return;
        } else {
            String url = file.getStr("url").toString();
            String filePath;
            if ("gzhj".equals(file.get("code"))) {
                filePath = PropKit.get("AppUploadPath") + url;
            } else if ("pp".equals(file.get("code"))) {
                filePath = url;
            } else {
                filePath = PropKit.get("baseUploadPath") + url;
            }
            File f = new File(filePath);
            renderFile(f);
        }
    }


    /**
     * 获取文件绝对路径
     * @param attachId
     * @return
     */
    public static String getAbsoluteFileUrlById(String attachId) {
        Record file = Db.findFirst("select * from base_attach where id = ?", attachId);
        if (file != null) {
            String url = file.getStr("url");
            String filePath;
            if ("gzhj".equals(file.get("code"))) {
                filePath = PropKit.get("AppUploadPath") + url;
            } else if ("pp".equals(file.get("code"))) {
                filePath = url;
            } else {
                filePath = PropKit.get("baseUploadPath") + url;
            }
            return filePath;
        }
        return null;
    }

    /**
     * 获取文件相对路径
     * @param attachId
     * @return
     */
    public static String getRelativeFileUrlById(String attachId) {
        return Db.queryStr("select url from base_attach where id = ?", attachId);
    }

    public void deleteFile() {
        String fileId = getPara("fileId");
        String table = getPara("table");
        String attach = getPara("attach");
        Record file = Db.findFirst("select * from base_attach where id = ?", fileId);
        final Ret ret = Ret.fail("message", "文件删除失败!");
        if (Func.isEmpty(file)) {
            ret.set("message", "找不到文件!");
        } else {
            String url = file.getStr("url").toString();
            File f = new File(PropKit.get("baseUploadPath") + url);
            if (f.exists()) {
                Db.tx(() -> {
                    boolean flag = f.delete();
                    if (flag) {
                        Db.deleteById("base_attach", fileId);
                        ret.setOk().set("message", "文件删除成功!");
                    }
                    //删除成功后,记录附件删除信息
                    Long userId = getLoginAccountId();
                    Record attachDel = new Record();
                    attachDel.set("attachid", fileId).set("userId", file.getStr("creater"))
                            .set("deluserId", userId).set("delTime", new Date())
                            .set("tableName", file.getStr("tabelname"))
                            .set("pkId", file.getStr("attachid"))
                            .set("typeCode", file.getStr("typecode"))
                            .set("code", file.getStr("code"));
                    DbService.save("base_attach_del", attachDel);
                    String resultId = Db.queryStr("select id from " + table + " where " + attach + " = ?", fileId);
                    if (resultId != null) {
                        Db.update("update " + table + " set " + attach + "=null where id=? ", resultId);
                    }
                    return true;
                });
            } else {
                ret.setOk().set("message", "丢失文件清除成功!");
            }
        }
        renderJson(ret);
    }
}
